//###############
//FILE : AddCommand.java
//WRITER : Daniel Lowengrub, daniel_i_l, 320687098
//EXERCISE : oop ex3 2011
//DESCRIPTION:
//See below
//###############
package oop.ex3.filemanager;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import oop.ex3.communication.SocketAddress;
import oop.ex3.protocol.sessions.FileReceiveSession;
import oop.ex3.protocol.sessions.FileWantFileSession;

/**
 * This runs the ADD command as specified in the assignment.
 * @author daniel
 */
public class AddCommand extends ModifyingCommand {

    private static final String ADD_COMMAND = "ADD";
    private static final String FAIL_ERROR = "Downloading failed";
    private static final String EXISTING_ERROR = "File already exists";
    private static final String SUCCESS_MSG = "File Downloaded Successfully from %s:%d\n";
    private static final int FILENAME_ARG = 1;


    /**
     * Construct an add command.
     * @param connection The connection this command uses to talk to a NS.
     */
    public AddCommand(NameServerConnection connection) {
        super(ADD_COMMAND, connection);
    }

    @Override
    public boolean executeCommand(String command) {
        command = command.trim();
        String fileName = command.split(COMMAND_SPACER)[FILENAME_ARG];

        if (_fileDatabase.hasFile(fileName)) {
            System.out.println(EXISTING_ERROR);
            return true;
        }
        
        //start looking through all the nameservers
        _nameServers.initServerSearch();
        ArrayList<SocketAddress> activeSet = _nameServers.getActiveSet();
        //System.out.println("Starting server search");
        
        while (activeSet.size() != 0) {
            for (SocketAddress address : activeSet)
                if(retreiveFileFromServer(address, fileName))
                    return true;

            _nameServers.updateActiveSet(_connection);
            activeSet = _nameServers.getActiveSet();
        }

        //no files were found
        System.out.println(FAIL_ERROR);
        return true;
    }

    //try to get a file from a specific NS.
    private boolean retreiveFileFromServer(SocketAddress nameServer, String fileName) {
    	//System.out.println("getting file from server: " + nameServer.getPort());
        ArrayList<SocketAddress> fileManagers = new ArrayList<SocketAddress>();
        //first get a list of file managers
        if (!_connection.initializeStreams(nameServer))
            return false;
        
        //System.out.println("sending want file to " + nameServer.getPort());
        FileWantFileSession wantFile = new FileWantFileSession();
        wantFile.setFileName(fileName);
        _connection.initializeSession(wantFile);
        
        if (!_connection.executeSession(wantFile))
            return false;
        
        _connection.closeConnection();
        
        //System.out.println("finished want file to " + nameServer.getPort());
        fileManagers = wantFile.getFileServers();
        
        //now try to get the file from on of the managers
        for (SocketAddress fileManager : fileManagers) {
            if (retreiveFileFromManager(fileManager, fileName))
                return true;
        }

        return false; //no file was found
    }

    //try to get a file from a specific FS.
    private boolean retreiveFileFromManager(SocketAddress fileManager, String fileName) {
        if (!_connection.initializeStreams(fileManager))
            return false;

        FileOutputStream fileOutput = _fileDatabase.createNewFile(fileName);
        DataOutputStream dataOutput = new DataOutputStream(new BufferedOutputStream(fileOutput));
        FileReceiveSession wantFile = new FileReceiveSession();
        wantFile.setFileName(fileName);
        wantFile.setFileOutput(dataOutput);

        if (!_connection.executeSession(wantFile)) {
            _fileDatabase.removeFile(fileName); //the file wasn't completed
            _connection.closeConnection();
            try {
                dataOutput.close();
            } catch (IOException ex) {
                //nothing left to do
            }
            return false;
        }

        _connection.closeConnection();

        //try to write the file to disk
        try {
            dataOutput.close();
        } catch (IOException ex) {
            //we couldn't write the file
            _fileDatabase.removeFile(fileName);
            return false;
        }

        //if we got the file the cleanup
        _fileDatabase.setCreationStatus(fileName, true); //we finished the creation
        
        //notify all nameservers
       // _nameServers.endServerSearch();
        for (SocketAddress nameServer : _nameServers.getAddressList())
            notifyNameServerAddition(nameServer, fileName);


        System.out.printf(SUCCESS_MSG, fileManager.getIp(), fileManager.getPort());
        return true;
    }

}
